#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <map>
#include <set>

#include <sys/types.h>
#include <sys/stat.h>

#include "pmu.h"       // libkperf
#include "symbol.h"    // libkperf

#include "interface.h"
#include "tuner.h"
#include "utils.h"

// 可以复用libpmu.so的采样数据，但不显式依赖该插件（因为可以预置profile来优化）
// 注意如果oeaware-collector仓库对应采样实例名字有变化时，此处也要同步修改
#define PMU_CYCLES_SAMPLING "pmu_cycles_sampling"
#define TUNER_INSTANCE_NAME "cfgo_tuner_sysboost"

// 记录插件enable之后处理的总samples数
uint64_t processed_samples = 0;

const char *sysboost_get_version()
{
    return "v1.0";
}

const char *sysboost_get_description()
{
    return "cfgo tuner: sysboost";
}

int sysboost_get_priority()
{
    return 0;
}

int sysboost_get_type()
{
    return -1;
}

const char *sysboost_get_dep()
{
    // 本插件启动时，不依赖采样插件，确保兼容使用预置profile的场景
    // 本插件启动后，增加对PMU_CYCLES_SAMPLING的依赖，方便获取ringbuf数据
    // TODO：增加一个插件状态：loaded/enabled/disabled
    return configs != nullptr ? PMU_CYCLES_SAMPLING : nullptr;
}

const char *sysboost_get_name()
{
    return TUNER_INSTANCE_NAME;
}

// 每隔多少ms执行一次run
int sysboost_get_period()
{
    return configs != nullptr ? configs->tuner_check_period : 1000;
}

bool sysboost_enable()
{
    logger.init();
    INFO("---------------------------------------------------------------");

    if (configs == nullptr &&
        parse_cfgo_ini(DEFAULT_CFGO_CONFIG_PATH) != 0) {
        ERROR("[enable] instance " << TUNER_INSTANCE_NAME << " init configs failed");
        return false;
    }

    if (configs->app == nullptr) {
        ERROR("[enable] no app in configs to be tunerd");
        return false;
    }
    
    auto result = exec_cmd("systemctl is-active sysboost");
    if (result.cmd_log != "active\n") {
        ERROR("[enable] invalid sysboost status: " << result.cmd_log);
        return false;
    }
    
    processed_samples = 0;

    INFO("[enable] plugin " << TUNER_INSTANCE_NAME << " enabled");
	return true;
}

void sysboost_disable()
{
    for (auto app = configs->app; app != nullptr; app = app->next) {
        if (app->status != OPTIMIZED) {
            continue;
        }
        exec_cmd("sysboostd --stop=" + app->full_path);
    }

    cleanup_configs();
    INFO("[disable] plugin " << TUNER_INSTANCE_NAME << " disabled");
}

const struct DataRingBuf *sysboost_get_ring_buf()
{
    // 调优插件不需要向其他插件提供数据
    return nullptr;
}

std::string get_app_profile(app_config *app)
{
    struct stat fileStat;

    // 使用最新的采样数据进行优化
    if (app->profile_path != "" &&
        stat(app->profile_path.c_str(), &fileStat) == 0 &&
        fileStat.st_size > 0) {
        DEBUG("[run] using the latest collected profile: " << app->profile_path);
        return app->profile_path;
    }
    // 使用默认的采样数据进行优化
    if (app->default_profile != "" &&
        stat(app->default_profile.c_str(), &fileStat) == 0 &&
        fileStat.st_size > 0) {
        DEBUG("[run] using the default profile: " << app->default_profile);
        return app->default_profile;
    }
    
    ERROR("[run] no invalid profile found for " << app->app_name);
    return "";
}

void update_app_profile_data(app_config *app, struct PmuData &data)
{
    if (data.ts < app->profile.ts) {
        DEBUG("[run] wrong timestamp of data");
        app->profile.ts = 0;
        return;
    } else if (data.ts - app->profile.ts > configs->collector_data_aging_time) {
        // 超过老化时间，丢弃历史数据
        app->profile.addrs.clear();
        app->profile.funcs.clear();
        app->profile.ts = data.ts;
        DEBUG("[run] clear old data for " << app->app_name);
    }

    auto &addrs = app->profile.addrs;
    auto &funcs = app->profile.funcs;

    unsigned long addr = data.stack->symbol->addr;
    if (addrs.find(addr) != addrs.end()) {
        addrs[addr].count++;
        funcs[addrs[addr].name][data.stack->symbol->offset]++;
    } else {
        addrs[addr] = AddrInfo();
        if (data.stack->symbol->mangleName != nullptr) {
            addrs[addr].name = data.stack->symbol->mangleName;
        } else {
            auto sym = SymResolverMapAddr(data.pid, addr);
            addrs[addr].name = sym->mangleName;
        }
        addrs[addr].count = 1;
        funcs[addrs[addr].name][data.stack->symbol->offset] = 1;
    }

    // if (data.stack->symbol->mangleName != nullptr) {
    //     auto sym = SymResolverMapAddr(data.pid, addr);
    //     if (sym->mangleName != data.stack->symbol->mangleName) {
    //         ERROR("[run] SymResolverMapAddr wrong, "
    //             << "mangleName: " << data.stack->symbol->mangleName << ", "
    //             << "sym->mangleName: " << sym->mangleName);
    //     }
    // }
}

// 获取profile中的函数数量，函数+偏移数量，以及有效sample数
void get_profile_func_offset_samples(const Profile &profile,
    int *funcs, int *offsets, int *samples) {
    *funcs = profile.funcs.size();
    *offsets = 0;
    *samples = 0;

    for (const auto& [func_name, offset_map] : profile.funcs) {
        *offsets += offset_map.size();
        for (const auto& [offset, count] : offset_map) {
            *samples += count;
        }
    }
}

// 判断是否需要将profile数据导出到文件
bool need_flush_app_profile_to_file(app_config *app)
{
    int funcs = 0, offsets = 0, samples = 0;
    get_profile_func_offset_samples(app->profile, &funcs, &offsets, &samples);

    DEBUG("[run] current stored data line cnt for " << app->app_name << ": " << offsets);

    // 超过20000行数据后可以有效优化app
    return offsets >= configs->collector_dump_data_threshold;
}

// 将profile数据导出到文件
void dump_app_profile_to_file(app_config *app)
{
    if (configs->tuner_optimizing_strategy == 0 && app->status == OPTIMIZED) {
        app->profile.addrs.clear();
        app->profile.funcs.clear();
        app->profile.ts = 0;
        return;
    }
    
    INFO("[run] dump profile data to " << app->profile_path);
    std::lock_guard<std::mutex> lock(app->profile_mtx);
    FILE *fp = fopen(app->profile_path.c_str(), "w");
    if (fp == nullptr) {
        ERROR("[run] fopen " << app->profile_path << " error");
    }
    fprintf(fp, "no_lbr cycles:u:\n");
    for (auto it1 = app->profile.funcs.begin(); it1 != app->profile.funcs.end(); ++it1) {
        for (auto it2 = it1->second.begin(); it2 != it1->second.end(); ++it2) {
            fprintf(fp, "1 %s %lx %d\n", it1->first.c_str(), it2->first, it2->second);
        }
    }
    fclose(fp);

    // 更新app状态
    if ((configs->tuner_optimizing_strategy == 0 && app->status != OPTIMIZED) ||
        configs->tuner_optimizing_strategy == 1) {
        app->status = NEED_OPTIMIZED;
    }

    app->profile.addrs.clear();
    app->profile.funcs.clear();
    app->profile.ts = 0;
}

// 处理pmu采样数据
void process_pmudata(struct PmuData *data, int len)
{
    // 1. 根据data中的pid判断app
    // 2. 判断现存数据的ts，如果ts在老化时间阈值前，则丢弃历史数据，并处理当前数据，刷新ts；如果ts未到阈值则处理当前数据
    // 3. 数据处理完成后，如果现存数据可以有效优化（足够多），则导出数据作为最新profile，导出后丢弃内存中的数据，并置needtuned为true
    // TODO: 待优化，不同ts的数据单独存放，老化时仅丢弃超过老化时间的数据
    // app->profile内部结构：
    // {
    //     最早一条profile数据的timestamp
    //     int64_t ts;
    //     地址信息，减少符号解析过程
    //     std::map<unsigned long, AddrInfo> addrs;
    //       {<addr>: {<name>,<offset>,<count>}, ...}
    //     函数信息，编译统计和导出
    //     std::map<std::string, std::map<unsigned long, int>> funcs;
    //       {<name>: {<offset>: <count>, ...}, ...}
    // }

    std::set<app_config*> updated_apps;
    for (int i = 0; i < len; ++i) {
        auto app = configs->app;
        // TODO:这个判断性能比较差，需要想办法加速，比如判断pid等等，避免字符串比较
        while (app != nullptr && strcmp(app->app_name.c_str(), data[i].comm) != 0) {
            app = app->next;
        }
        if (app == nullptr ||                   // 未匹配到app，直接跳过
            data[i].stack == nullptr ||         // 空数据，直接跳过
            data[i].stack->symbol == nullptr) { // 空数据，直接跳过
            continue;
        }

        update_app_profile_data(app, data[i]);
        updated_apps.insert(app);
    }

    for (app_config* app : updated_apps) {
        if (!need_flush_app_profile_to_file(app)) {
            continue;
        }

        dump_app_profile_to_file(app);
    }
}

// 从collector获取ringbuf
void get_collector_ringbuf(
    const struct Param *param, const struct DataRingBuf **ringbuf, uint64_t *cnt) {
    const struct DataRingBuf *buf = nullptr;
    static int last_record_index = -1;     // 记录上一次处理的ring_bufs下标
    static uint64_t last_record_count = 0; // 记录上一次处理时采集插件的运行次数

    *ringbuf = nullptr;
    *cnt = 0;

    // for (int i = 0; i < param->len; i++) {
    //     auto tmp = param->ring_bufs[i];
    //     // 部分插件返回空ringbuf信息
    //     if (tmp == nullptr) {
    //         continue;
    //     }

    //     DEBUG("[run] instance_name: " << tmp->instance_name << ", index: "
    //         << tmp->index << ", count: " << tmp->count << ", buf_len: " << tmp->buf_len);
    // }

    // 如果插件无变化，可以快速找到对应param
    if (last_record_index >= 0 && last_record_index < param->len &&
        param->ring_bufs[last_record_index] != nullptr &&
        strcmp(param->ring_bufs[last_record_index]->instance_name, PMU_CYCLES_SAMPLING) == 0) {
        buf = param->ring_bufs[last_record_index];
    } else {
        for (int i = 0; i < param->len; i++) {
            if (param->ring_bufs[i] == nullptr) {
                continue;
            }
            if (strcmp(param->ring_bufs[i]->instance_name, PMU_CYCLES_SAMPLING) == 0) {
                buf = param->ring_bufs[i];
                last_record_index = i;
                break;
            }
        }
    }

    if (buf == nullptr) {
        last_record_index = -1;
        last_record_count = 0;
        return;
    }

    if (buf->count > last_record_count) {
        // 数据有更新，注意DataBuf已经全部刷新的场景
        *ringbuf = buf;
        *cnt = std::min(buf->count - last_record_count, (uint64_t)buf->buf_len);
        last_record_count = buf->count;
    } else if (buf->count < last_record_count) {
        // 异常场景
        WARN("[run] record data count: " << last_record_count
            << " is large than " << "current count: " << buf->count);
        *ringbuf = nullptr;
        last_record_count = 0;
    }
}

void get_sampling_data_from_collector(const struct Param *param) {
    const struct DataRingBuf *ringbuf = nullptr;
    uint64_t cnt = 0; // 需要处理的DataBuf的个数，小于buf_len
    
    get_collector_ringbuf(param, &ringbuf, &cnt);
    if (ringbuf == nullptr || cnt == 0) {
        return;
    }

    // DEBUG("[run] process " << ringbuf->instance_name << " ringbuf: " << ringbuf << ", databuf cnt: " << cnt);

    // 从ringbuf->index开始，倒序处理cnt个DataBuf，同时校验PmuData的ts；
    for (int i = 0; i < (int)cnt; i++) {
        int index = (ringbuf->buf_len + ringbuf->index - i) % ringbuf->buf_len;
        // DEBUG("[run]   process buf[" << index << "]: " << ringbuf->buf[index].len << " pmudata");
        process_pmudata((struct PmuData *)(ringbuf->buf[index].data), ringbuf->buf[index].len);
        processed_samples += (uint64_t)ringbuf->buf[index].len;
    }

    DEBUG("[run] processed pmudata in total (since last enable): " << processed_samples);
}

// 调用sysboostd进行优化
void do_optimize(app_config *app, std::string profile) {
    const std::string default_bolt_options =
        "-reorder-blocks=ext-tsp -reorder-functions=hfsort+ "
        "-split-functions -split-all-cold -dyno-stats -icf=1 "
        "-use-gnu-stack --inline-all --hugify";

    INFO("[run] try to optimize app: " << app->app_name);

    exec_cmd("sysboostd --stop=" + app->full_path);

    exec_cmd("sysboostd --gen-bolt=" + app->full_path + " --bolt-option=\"" +
             (app->bolt_options == "" ? default_bolt_options : app->bolt_options) +
             "\" --profile-path=" + profile);
    app->status = OPTIMIZED;
    app->profile.addrs.clear();
    app->profile.funcs.clear();
    app->profile.ts = 0;
}

bool is_app_eligible_for_optimization(app_config *app)
{
    if (app->status != NEED_OPTIMIZED) {
        return false;
    }

    if (configs->tuner_optimizing_condition == 0) {
        if (get_target_pid(app) > 0) {
            return false;
        }
        return true;
    }

    return false;
}

void sysboost_run(const struct Param *param)
{
    // 周期性轮询，如果有采样数据，且进程未启动，则触发优化
    // 1. 刷新采样数据
    // 2. 检查目标应用二进制，如果应用正在运行中则直接返回（不满足优化条件）
    // 3. 确认profile位置，校验文件
    // 4. 调用sysboostd优化
	static bool optimizing = false;

    if (configs == nullptr) {
        ERROR("[run] no valid configs found");
        return;
    }

    // 防止第一轮优化还未结束就触发新一轮优化
	if (optimizing) {
        DEBUG("[run] last optimizing is not finished, skip");
		return;
	}

	optimizing = true;

    // step1: 刷新采样数据
    get_sampling_data_from_collector(param);

    for (auto app = configs->app; app != nullptr; app = app->next) {
        // step2: 检查应用是否满足优化条件
        if (!is_app_eligible_for_optimization(app)) {
            continue;
        }

        // step3: 获取profile文件
        std::lock_guard<std::mutex> lock(app->profile_mtx);
        std::string profile_path = get_app_profile(app);
        if (profile_path == "") {
            continue;
        }

        // step4: 优化
        do_optimize(app, profile_path);
    }

	optimizing = false;
}

struct Interface sysboost_tuner = {
    .get_version     = sysboost_get_version,
    .get_name        = sysboost_get_name,
    .get_description = sysboost_get_description,
    .get_dep         = sysboost_get_dep,
    .get_priority    = sysboost_get_priority,
    .get_type        = sysboost_get_type,
    .get_period      = sysboost_get_period,
    .enable          = sysboost_enable,
    .disable         = sysboost_disable,
    .get_ring_buf    = sysboost_get_ring_buf,
    .run             = sysboost_run,
};
